#!/usr/bin/env python
# -*- encoding=utf-8 -*-
'''
Created on 2017/12/28

@author: peulerosci
'''

import logging
import json, time
import logging.config
import requests
import cve_db
import xml.etree.ElementTree as ET
from subprocess import *
import os, re, sys, datetime
import tempfile
reload(sys)
sys.setdefaultencoding('utf8')

if not os.path.isdir("log"):
    os.mkdir("log")
logging.config.fileConfig("logger.conf")
logger = logging.getLogger("copy_jenkins")

if len(sys.argv) >= 4:
    BRANCH = sys.argv[2]
    PROJECT = sys.argv[3]
    CHANGE_ID = sys.argv[1]
    CHANGE_ID_1 = sys.argv[4]
else:
    BRANCH = os.getenv('GERRIT_BRANCH')
    PROJECT = os.getenv('GERRIT_PROJECT')
    CHANGE_ID = os.getenv('GERRIT_CHANGE_NUMBER')
    CHANGE_ID_1 = os.getenv('GERRIT_CHANGE_ID')
REPO_DIR = '/usr1/jenkins/EulerOS_' + BRANCH

error_msg = '''
Please check you changelog, must be:
* Week Mon Date Year Author<euleros@huawei.com> - 1.0-1
- Type:security etc.
- ID:CVE-2007-3568
- SUG:restart etc.
- DESC:Describe the problem in detail

for detail: http://plm-pia.huawei.com/Windchill/DBOX/docdetail/pageinfo.jsp?oid=VR:wt.doc.WTDocument:11587660775
'''


def shell_cmd(s_cmd_line, inmsg=None):
    p = Popen(s_cmd_line, stdin=PIPE, stdout=PIPE, stderr=PIPE, shell=True)
    if inmsg:
        p.stdin.write(inmsg)
    out, err = p.communicate()
    return p.returncode, out, err


def get_project_path():
    default_xml = REPO_DIR + '/.repo/manifest.xml'
    with open(default_xml, 'r') as myfile:
        mystr = myfile.read()
        tmppath = re.findall('<project name="{0}"\s+path="(.+)"'.format(PROJECT), mystr)
        if len(tmppath) == 1:
            mypath = tmppath[0]
            return mypath

        xml_list = re.findall(r'<include name="(.+)"/>', mystr)
        mypath = ''
        for next_xml in xml_list:
            with open(REPO_DIR + '/.repo/manifests/' + next_xml) as myxml:
                tmp_str = myxml.read()
                tmppath = re.findall('<project name="{0}"\s+path="(.+)"'.format(PROJECT), tmp_str)
                if len(tmppath) == 1:
                    mypath = tmppath[0]
                    #print '8888888888888============{0}'.format(mypath)
                    break
        if mypath == '':
            logger.error('can not find this git project: {0}'.format(PROJECT))
            sys.exit(1)

        return mypath


def get_spec_info():
    project_dir = REPO_DIR + '/' + get_project_path()

    # 根据changeid获取对应的commitid
    cmd = 'cd {0};git log --no-merges'.format(project_dir)
    ret, out, err = shell_cmd(cmd)
    if ret != 0:
        logger.error("run: %s : failed,the out is %s,the err is %s" % (cmd, out, err))
        sys.exit(1)
    commit_id = out.split(CHANGE_ID_1)[0].split('Change-Id:')[-2].split('Author:')[0].split('commit')[-1].split('\n')[0].replace('\r', '').replace(' ', '')

    # 切换到对应的提交点
    cmd = 'cd {0};git checkout {1}'.format(project_dir, commit_id)
    ret, out, err = shell_cmd(cmd)
    if ret != 0:
        logger.error("run: %s : failed,the out is %s,the err is %s" % (cmd, out, err))
        sys.exit(1)

    # 获取当前提交的spec的修改情况，如果没有spec的变更或changelog里面不是安全相关的就直接成功退出。如果是安全相关的就获取相关信息
    cmd = 'cd {0};git diff --name-status HEAD~1 HEAD~0'.format(project_dir)
    ret, out, err = shell_cmd(cmd)
    if ret != 0:
        logger.error("run: %s : failed,the out is %s,the err is %s" % (cmd, out, err))
        sys.exit(1)
    spec_list = re.findall('M\s(.+\.spec)', out)
    if len(spec_list) == 0:
        logger.info('No spec changed, so exit as success')
        sys.exit(0)

    cve_list = []
    rpm_list = []
    for spec in spec_list:
        logger.info('spec is ====== {0}'.format(spec))
        with open(project_dir + '/' + spec) as myfile:
            new_spec = myfile.read()

        new_changelog = re.findall('.*\n%changelog\s*\n(.+)', new_spec, re.S)
        if len(new_changelog) != 1:
            logger.error('There is no changelog in spec: {0}, num ==== {1}'.format(spec, len(new_changelog)))
            sys.exit(1)
        new_log = new_changelog[0].split('\n\n*')[0]
        logger.info('new changelog is ================= {0}'.format(new_log))
        type_value = re.findall(r'- ID:(.+?)\n', new_log, re.S)[0].lstrip().rstrip()
        if ',' in type_value:
            cves = type_value.split(',')
        else:
            cves = type_value.split()
        for cve in cves:
            if cve not in cve_list and re.match('^CVE-', cve):
                cve_list.append(cve)

        # get src rpm
        cmd = "rpm -q --qf '%{{NAME}}-%{{VERSION}}-%{{RELEASE}}.src.rpm\n' --specfile {0}".format(
            project_dir + '/' + spec)
        ret, out, err = shell_cmd(cmd)
        if ret != 0 and re.match(r'^error: Group ', err):
            with open(project_dir + '/' + spec, 'r') as my_f:
                tmpstr = my_f.read()
                tmpstr1 = re.sub(r'(%package|Name:)(.*)', r'\1\2\nGroup:xdf', tmpstr, re.M)
            a = tempfile.mktemp()
            with open(a, 'w') as b:
                b.write(tmpstr1)
                b.flush()
            cmd = "rpm -q --qf '%{{NAME}}-%{{VERSION}}-%{{RELEASE}}.src.rpm\n' --specfile {0}".format(a)
            ret, out, err = shell_cmd(cmd)
            if ret != 0:
                print "run: %s : failed,the out is %s,the err is %s" % (cmd, out, err)
                out = 'read spec failed by: rpm -q --specfile'
            os.remove(a)
        elif ret != 0 and not re.match(r'^error: Group ', err):
            print "run: %s : failed,the out is %s,the err is %s" % (cmd, out, err)
            out = 'read spec failed by: rpm -q --specfile'
        src_rpm = out.split('\n')[0]
        if src_rpm not in rpm_list:
            rpm_list.append(src_rpm)
        # get rpm list from spec
        cmd = "rpm -q --qf '%{{NAME}}-%{{VERSION}}-%{{RELEASE}}.%{{ARCH}}.rpm\n' --specfile {0}".format(project_dir + '/' + spec)
        ret, out, err = shell_cmd(cmd)
        if ret != 0 and re.match(r'^error: Group ', err):
            with open(project_dir + '/' + spec, 'r') as my_f:
                tmpstr = my_f.read()
                tmpstr1 = re.sub(r'(%package|Name:)(.*)', r'\1\2\nGroup:xdf', tmpstr, re.M)
            a = tempfile.mktemp()
            with open(a, 'w') as b:
                b.write(tmpstr1)
                b.flush()
            cmd = "rpm -q --qf '%{{NAME}}-%{{VERSION}}-%{{RELEASE}}.%{{ARCH}}.rpm\n' --specfile {0}".format(a)
            ret, out, err = shell_cmd(cmd)
            if ret !=0:
                print "run: %s : failed,the out is %s,the err is %s" % (cmd, out, err)
                out = 'read spec failed by: rpm -q --specfile'
            os.remove(a)
        elif ret !=0 and not re.match(r'^error: Group ', err):
            print "run: %s : failed,the out is %s,the err is %s" % (cmd, out, err)
            out = 'read spec failed by: rpm -q --specfile'
        for tmp in out.split('\n'):
            if not re.match('.*\.rpm', tmp):
                continue
            if tmp not in rpm_list:
                rpm_list.append(tmp)

    logger.info('cve id is ================= {0}'.format(cve_list))
    if len(cve_list) == 0:
        logger.info('No cve or security changed, so exit as success')
        sys.exit(0)

    # 切换到上一个提交点获取相关信息
    # 切换到对应的提交点
    cmd = 'cd {0};git checkout HEAD~1'.format(project_dir)
    ret, out, err = shell_cmd(cmd)
    if ret != 0:
        logger.error("run: %s : failed,the out is %s,the err is %s" % (cmd, out, err))
        sys.exit(1)
    old_rpm_list = []
    old_rpm_name = ''
    for spec in spec_list:
        logger.info('old spec is ====== {0}'.format(spec))
        # get src rpm
        cmd = "rpm -q --qf '%{{NAME}}-%{{VERSION}}-%{{RELEASE}}.src.rpm\n' --specfile {0}".format(
            project_dir + '/' + spec)
        ret, out, err = shell_cmd(cmd)
        if ret != 0 and re.match(r'^error: Group ', err):
            with open(project_dir + '/' + spec, 'r') as my_f:
                tmpstr = my_f.read()
                tmpstr1 = re.sub(r'(%package|Name:)(.*)', r'\1\2\nGroup:xdf', tmpstr, re.M)
            a = tempfile.mktemp()
            with open(a, 'w') as b:
                b.write(tmpstr1)
                b.flush()
            cmd = "rpm -q --qf '%{{NAME}}-%{{VERSION}}-%{{RELEASE}}.src.rpm\n' --specfile {0}".format(a)
            ret, out, err = shell_cmd(cmd)
            if ret != 0:
                print "run: %s : failed,the out is %s,the err is %s" % (cmd, out, err)
                out = 'read spec failed by: rpm -q --specfile'
            os.remove(a)
        elif ret != 0 and not re.match(r'^error: Group ', err):
            print "run: %s : failed,the out is %s,the err is %s" % (cmd, out, err)
            out = 'read spec failed by: rpm -q --specfile'
        old_src_rpm = out.split('\n')[0]
        if old_src_rpm not in old_rpm_list:
            old_rpm_list.append(old_src_rpm)
        # get rpm list from spec
        cmd = "rpm -q --qf '%{{NAME}}-%{{VERSION}}-%{{RELEASE}}.%{{ARCH}}.rpm\n' --specfile {0}".format(
            project_dir + '/' + spec)
        ret, out, err = shell_cmd(cmd)
        if ret != 0 and re.match(r'^error: Group ', err):
            with open(project_dir + '/' + spec, 'r') as my_f:
                tmpstr = my_f.read()
                tmpstr1 = re.sub(r'(%package|Name:)(.*)', r'\1\2\nGroup:xdf', tmpstr, re.M)
            a = tempfile.mktemp()
            with open(a, 'w') as b:
                b.write(tmpstr1)
                b.flush()
            cmd = "rpm -q --qf '%{{NAME}}-%{{VERSION}}-%{{RELEASE}}.%{{ARCH}}.rpm\n' --specfile {0}".format(a)
            ret, out, err = shell_cmd(cmd)
            if ret != 0:
                print "run: %s : failed,the out is %s,the err is %s" % (cmd, out, err)
                out = 'read spec failed by: rpm -q --specfile'
            os.remove(a)
        elif ret != 0 and not re.match(r'^error: Group ', err):
            print "run: %s : failed,the out is %s,the err is %s" % (cmd, out, err)
            out = 'read spec failed by: rpm -q --specfile'
        for tmp in out.split('\n'):
            if not re.match('.*\.rpm', tmp):
                continue
            if tmp not in old_rpm_list:
                old_rpm_list.append(tmp)

        if old_rpm_name == '':
            cmd = "rpm -q --qf '%{{NAME}}\n' --specfile {0}".format(project_dir + '/' + spec)
            ret, out, err = shell_cmd(cmd)
            if ret != 0 and re.match(r'^error: Group ', err):
                with open(project_dir + '/' + spec, 'r') as my_f:
                    tmpstr = my_f.read()
                    tmpstr1 = re.sub(r'(%package|Name:)(.*)', r'\1\2\nGroup:xdf', tmpstr, re.M)
                a = tempfile.mktemp()
                with open(a, 'w') as b:
                    b.write(tmpstr1)
                    b.flush()
                cmd = "rpm -q --qf '%{{NAME}}\n' --specfile {0}".format(a)
                ret, out, err = shell_cmd(cmd)
                if ret != 0:
                    print "run: %s : failed,the out is %s,the err is %s" % (cmd, out, err)
                    out = 'read spec failed by: rpm -q --specfile'
                os.remove(a)
            elif ret != 0 and not re.match(r'^error: Group ', err):
                print "run: %s : failed,the out is %s,the err is %s" % (cmd, out, err)
                out = 'read spec failed by: rpm -q --specfile'
            old_rpm_name = out.split('\n')[0]

    old_rpms = '\n'.join(old_rpm_list)
    old_rpms = old_rpms.replace('%{dist}', '')
    rpms = '\n'.join(rpm_list)
    rpms = rpms.replace('%{dist}', '')

    # 检查是不是aarch64架构的
    if PROJECT == 'euleros/third_party/open_source/kernelspace/linux-4.19' or re.match('.*-aarch64', PROJECT):
        old_rpms = old_rpms.replace('.x86_64.rpm', '.aarch64.rpm')
        rpms = rpms.replace('.x86_64.rpm', '.aarch64.rpm')

    logger.info('cve list: {0}\nold_rpm_name: {1}\nold_rpm_list: {2}\nrpm_list: {3}\n'.format(cve_list, old_rpm_name, old_rpms, rpms))
    return cve_list, old_rpm_name, old_rpms, rpms


def get_commit_info():
    cmd = 'ssh -p 29418 peulerosci@dgggit09-rd.huawei.com gerrit query --current-patch-set \
        --patch-sets --format=JSON change:"%s"' % (CHANGE_ID)
    ret, out, err = shell_cmd(cmd)
    if ret != 0:
        logger.error("run: %s : failed,the out is %s,the err is %s" % (cmd, out, err))
        # return False
        sys.exit(1)

    line = out.split(']}\n')[0].strip()
    line = line.strip(',') + ']}'
    #logger.warning('1111111111111111111111111======{0}\n\n'.format(line))
    js = None
    try:
        js = json.loads(line)
    except Exception, e:
        logger.error('%s : %s' % (Exception, e))
        sys.exit(1)

    createdOn_tmp = js.get('createdOn')
    createdOn = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(int(createdOn_tmp)))
    currentpatch = js.get('currentPatchSet')
    if currentpatch:
        committers = currentpatch.get('author').get('name')

    commit = js.get('commitMessage')
    if commit:
        dts = re.findall(r'\[Require/DTS\]:(.+?)\n', commit, re.S)[0].lstrip().rstrip()

    logger.info('dts:{0}\nowner:{1}\ndatetime:{2}\n'.format(dts, committers, createdOn))
    return dts, committers, createdOn


def get_cve_info(cve):
    my_db = cve_db.db_cve()
    result = my_db.find_by_cveid(cve)
    logger.info('info from cve web====={0}'.format(result))
    score = None
    desc = None
    if result:
        if result[1] == None or result[1] == '':
            score = result[0]
        else:
            score = result[1]
        desc = result[2]
    return score, desc


if __name__ == "__main__":
    cve_list, rpm_name, old_rpms, rpms = get_spec_info()
    dts, committers, createdOn = get_commit_info()

    url = 'http://10.177.119.64/code/set_security_to_db'
    for cve_id in cve_list:
        score, desc = get_cve_info(cve_id)
        d = {'cve_id':cve_id, 'ChangeID':CHANGE_ID, 'Branch':BRANCH, 'Project':PROJECT, 'dts':dts, 'desc':desc,\
             'score':score, 'owner':committers, 'createdOn':createdOn, 'status':r'', 'version':'', \
             'rpm_name':rpm_name, 'old_rpms':old_rpms, 'rpm_status':'', 'rpms':rpms}
        logger.info('post data: {0}'.format(d))
        r = requests.post(url, data=d)

        result_html = open('result.html', 'w')
        result_html.write(r.text)
        #logger.info('text return from url: {0}'.format(r.text))
        if r.status_code != 200:
            logger.error('status return from url: {0}'.format(r.status_code))
            sys.exit(1)

